﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;

namespace Tools.Service.File
{
    /// <summary>
    /// 视频处理
    /// </summary>
    public class VideoService
    {
        /// <summary>
        /// 视频处理器ffmpeg.exe的位置
        /// </summary>
        private const string FFmpegPath = "E:\\办公软件\\ffmpeg-2022-12-11-git-c3e20f78b0-essentials_build\\ffmpeg-2022-12-11-git-c3e20f78b0-essentials_build\\bin\\ffmpeg.exe";

        /// <summary>
        /// 视频添加水印
        /// </summary>
        /// <param name="videoFile">视频路径</param>
        /// <param name="outputFile">水印添加之后路径</param>
        /// <param name="imgFile">图片水印路径</param>
        /// <param name="waterMarkName">水印文字</param>
        /// <param name="type">类型（img-图片,text-文字）</param>
        /// <param name="layout">布局（1-左上方，2顶部中间，3-右上方，4-左边，5-中间，6-右边，7-左下方，8-底部中间，9-右下方）</param>
        public void AddWatermark(string videoFile,string outputFile,string imgFile, string waterMarkName, string type, string layout)
        {
            var overlay = "";
            var mingling = "";
            if (type == "img")
            {
                switch (layout)
                {
                    case "1":
                        overlay = "overlay=5:5";
                        break;
                    case "3":
                        overlay = "overlay=main_w-overlay_w-5:5";
                        break;
                    case "5":
                        overlay = "overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2";
                        break;
                    case "7":
                        overlay = "overlay=5:main_h-overlay_h";
                        break;
                    case "9":
                        overlay = "overlay=main_w-overlay_w-5:main_h-overlay_h-5";
                        break;
                    default:
                        overlay = "overlay=5:5";
                        break;
                }
                mingling=$"-i {videoFile} -i {imgFile} -filter_complex \"{overlay}:1\"  {outputFile}";
            }
            else {
                switch (layout)
                {
                    case "1":
                        overlay = ":x=5:y=5";
                        break;
                    case "3":
                        overlay = ":x=w-text_w-5:y=5";
                        break;
                    case "5":
                        overlay = ":x=(w-text_w)/2:y=(h-line_h)/2";
                        break;
                    case "7":
                        overlay = ":x=5:y=h-line_h";
                        break;
                    case "9":
                        overlay = ":x=w-text_w-5:y=h-line_h-5";
                        break;
                    default:
                        overlay = ":x=5:y=5";
                        break;
                }
                mingling = $"-i {videoFile} -vf \"drawtext = fontsize = 40:fontfile = {AppDomain.CurrentDomain.BaseDirectory}Fonts\\SIMFANG.TTF:text = '{waterMarkName}'{overlay}:fontcolor = black:box = 1:boxcolor = white\" {outputFile}";
            }
            // 文字水印 ffmpeg -i test.mp4 -vf "drawtext=fontsize=100:fontfile=lazy.ttf:text='hello world':x=20:y=20:fontcolor=green:box=1:boxcolor=yellow" out.mp4
            // 图片水印 ffmpeg -i in.mp4 -vf "movie=logo.jpg,scale=50:10,colorkey=white:0.01:1.0 [wm]; [in] [wm] overlay=30:10 [out]" in_out_watermask_scale.mp4
            // 动图水印 ffmpeg -i in.mp4 -ignore_loop 0 -i dongtu.gif -filter_complex "[1:v]scale=192:108[wm];[0:v][wm]overlay=0:H-h:shortest=1" in_out_dynamic_watermark.mp4
            //string mingling = "-i " + videoFile + " -i " + imgFile + " -filter_complex \"overlay=10:10\" " + outputFile;
            

            CommandManager(mingling);
        }

        /// <summary>
        /// 添加水印,fileName视频地址,imgFile水印图片地址,outputFile输出地址(未实现)
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="imgFile"></param>
        /// <param name="outputFile"></param>
        /// <returns></returns>
        public static int WaterMark(string fileName, string imgFile, string outputFile)
        {
            //取得ffmpeg.exe的路径,路径配置在Web.Config中,如:<add   key="ffmpeg"   value="E:\aspx1\ffmpeg.exe"   />  
            fileName = "E:\\BaiduNetdiskDownload\\下午06-图标字体回顾.web_recv.avi";
            imgFile = "E:\\BaiduNetdiskDownload\\true.png";
            outputFile = "E:\\BaiduNetdiskDownload\\outputFile.avi";
            string ffmpeg = "C:\\Users\\Administrator\\Downloads\\ffmpeg-2021-04-25-git-d98884be41-full_build\\ffmpeg-2021-04-25-git-d98884be41-full_build\\bin\\ffmpeg.exe";
            if ((!System.IO.File.Exists(ffmpeg)) || (!System.IO.File.Exists(fileName)))
            {
                return 0;
            }
            //建立ffmpeg进程
            System.Diagnostics.ProcessStartInfo WaterMarkstartInfo = new System.Diagnostics.ProcessStartInfo(ffmpeg);
            //后台运行
            WaterMarkstartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
            //运行参数
            string config = "   -i   " + fileName + " -vf \"movie=" + imgFile + " [watermark]; [in][watermark] overlay=0:0 [out]\" " + outputFile;
            WaterMarkstartInfo.Arguments = config;
            try
            {
                //开始加水印
                System.Diagnostics.Process.Start(WaterMarkstartInfo);

            }
            catch
            {
                return 0;
            }

            return 1;
        }

        /// <summary>
        /// 调用ffmpeg.exe 执行命令
        /// </summary>
        /// <param name="Parameters">命令参数</param>
        /// <returns>返回执行结果</returns>
        private string RunProcess(string Parameters)
        {
            //创建一个ProcessStartInfo对象 并设置相关属性
            var oInfo = new ProcessStartInfo(FFmpegPath, Parameters);
            oInfo.UseShellExecute = false;
            oInfo.CreateNoWindow = true;
            oInfo.RedirectStandardOutput = true;
            oInfo.RedirectStandardError = true;
            oInfo.RedirectStandardInput = true;

            //创建一个字符串和StreamReader 用来获取处理结果
            string output = null;
            StreamReader srOutput = null;

            try
            {
                //调用ffmpeg开始处理命令
                var proc = Process.Start(oInfo);

                proc.WaitForExit();


                //获取输出流
                srOutput = proc.StandardError;

                //转换成string
                output = srOutput.ReadToEnd();

                //关闭处理程序
                proc.Close();
            }
            catch (Exception)
            {
                output = string.Empty;
            }
            finally
            {
                //释放资源
                if (srOutput != null)
                {
                    srOutput.Close();
                    srOutput.Dispose();
                }
            }
            return output;
        }

        /// <summary>
        /// 视频合并
        /// </summary>
        /// <param name="videoPathList">需合并的视频集合</param>
        /// <param name="saveFilePath">保存文件名</param>
        /// <returns></returns>
        public void VideoCombine(List<string> videoPathList, string saveFilePath)
        {
            var videoCmdList = new List<string>();
            var videoMerge = new StringBuilder();
            videoMerge.Append("-i \"concat:");

            foreach (var videoPathItem in videoPathList)
            {
                videoCmdList.Add($" -i {videoPathItem} -c copy -bsf:v h264_mp4toannexb -f mpegts {videoPathItem}.ts -y ");

                if (videoPathList.IndexOf(videoPathItem) == 0) 
                {
                    videoMerge.Append($"{videoPathItem}.ts");
                }
                else 
                {
                    videoMerge.Append($"|{videoPathItem}.ts");
                }
                
            }

            videoMerge.Append("\" -c copy -bsf:a aac_adtstoasc -movflags +faststart " + saveFilePath + " -y ");

            //1、转换文件类型，由于不是所有类型的视频文件都支持直接合并，需要先转换格式
            foreach (var videoCmdItem in videoCmdList)
            {
                CommandManager(videoCmdItem);
            }
     
            //2、视频合并
            CommandManager(videoMerge.ToString());
        }

        /// <summary>
        /// 音频合并
        /// </summary>
        /// <param name="audioPathList">需合并的音频集合</param>
        /// <param name="saveFilePath">保存文件路径</param>
        public void AudioMerge(List<string> audioPathList, string saveFilePath)
        {
            //将多个音频混合成一个音频文件输出 http://www.ffmpeg.org/ffmpeg-all.html#amix


            //ffmpeg -i INPUT1 -i INPUT2 -i INPUT3 -filter_complex amix=inputs=3:duration=first:dropout_transition=3 OUTPUT


            //合并两个音频
            //ffmpeg -i input1.mp3 -i input2.mp3 -filter_complex amerge -ac 2 - c:a libmp3lame -q:a 4 output.mp3


            //获取视频中的音频
            //ffmpeg -i input.mp4 -vn -y -acodec copy output.m4a


            //去掉视频中的音频
            //ffmpeg -i input.mp4 -an output.mp4


            // https://www.cnblogs.com/simadi/p/10649345.html
            // ffmpeg -i "concat:123.mp3|124.mp3" -acodec copy output.mp3
            // 解释：-i代表输入参数
            // contact: 123.mp3 | 124.mp3代表着需要连接到一起的音频文件 -acodec copy output.mp3 重新编码并复制到新文件中


            string commandStr = $"-i \"concat:{string.Join("|", audioPathList)}\" -acodec copy saveFilePath  -y";
            CommandManager(commandStr);
        }

        /// <summary>
        /// 获取视频中的音频
        /// <param name="videoFile">视频路径</param>
        /// <param name="saveFilePath">保存文件路径</param>
        /// </summary>
        public void VideGetAudio(string videoFile,string saveFilePath)
        {
            string commandStr = $"-i {videoFile} -vn -y -acodec copy {saveFilePath}";
            CommandManager(commandStr);
        }

        /// <summary>
        /// 去掉视频中的音频
        /// <param name="videoFile">视频路径</param>
        /// <param name="saveFilePath">保存文件路径</param>
        /// </summary>
        public void VideRemoveAudio(string videoFile, string saveFilePath)
        {
            string commandStr = $"-i {videoFile} -an {saveFilePath}";
            CommandManager(commandStr);
        }

        /// <summary>
        /// 音频与视频合并成视频
        /// <param name="videoPath">视频路径</param>
        /// <param name="audioPath">音频路径</param>
        /// <param name="saveFilePath">保存文件路径</param>
        /// </summary>
        public void AudioAndVideoMerge(string videoPath,string audioPath,string saveFilePath)
        {
            //1、视频文件中没有音频。
            //ffmpeg -i video.mp4 -i audio.wav -c:v copy -c:a aac -strict experimental output.mp4
            //string mergeCommandStr = $"-i {physicalPath}video2.mp4 -i {physicalPath}music1.mp3 -c:v copy -c:a aac -strict experimental {physicalPath}output.mp4  -y";


            //video.mp4,audio.wav分别是要合并的视频和音频，output.mp4是合并后输出的音视频文件。
            //2、下面的命令是用audio音频替换video中的音频 ffmpeg -i video.mp4 -i audio.wav -c:v copy -c:a aac -strict experimental -map 0:v:0 -map 1:a: 0 output.mp4
            string mergeCommandStr = $"-i {videoPath} -i {audioPath} -c:v copy -c:a aac -strict experimental -map 0:v:0 -map 1:a:0 {saveFilePath}  -y";


            //3、c++音频视频合并(视频文件中没有音频的情况下)
            //"ffmpeg -i /tmp/mergeMp3/392118469203595327/392118469203595327.aac  -i /tmp/mergeMp3/392118469203595327/bg.mp4 -c copy -bsf:a aac_adtstoasc /tmp/mergeMp3/392118469203595327/392118469203595327.mp4 -y"
            //string mergeCommandStr3 = $"-i {physicalPath}video5.mp4  -i {physicalPath}AudioMerge.mp3 -c copy -bsf:a aac_adtstoasc {physicalPath}AudioAndVideoMerge1.mp4 -y";


            CommandManager(mergeCommandStr);
        }

        /// <summary>
        /// 图片合并成视频
        /// </summary>
        /// <param name="total">文件数量</param>
        /// <param name="directoryPath">存放图片文件夹路径</param>
        /// <param name="newFileGuid">图片名称guid部分</param>
        /// <param name="saveFilePath">保存文件路径</param>
        public void ImagesToVideo(int total,string directoryPath,string newFileGuid, string saveFilePath,string audio=null,int time=0)
        {
            time = time == 0 ? total : time;
            var mergeCommandStr = $"-framerate {Math.Round(1/(double)(time/total),2)} -f image2 -loop 1 -i {directoryPath}\\{newFileGuid}_%d.jpeg {(!string.IsNullOrWhiteSpace(audio)?$"-i {audio}":"")} -vcodec libx264 -pix_fmt yuv420p -s 1080*1920 -r 25 -t {time} -y {saveFilePath}";
            //string mergeCommandStr = $"-f {string.Join(" ",pathList)} -vf scale=-1:480 {saveFilePath} #-1表示比例缩放，也可-vf scale=640:-1固定宽度缩放高度";
            //string mergeCommandStr = $"-y -framerate 1 -f image2 -i {directoryPath}\\{newFileGuid}_%d.jpeg -vf scale=-1:480 -s 800x800 -q:v 1  {saveFilePath}";

            CommandManager(mergeCommandStr);

        }

        /// <summary>
        /// 执行
        /// C# Process进程调用 https://docs.microsoft.com/zh-cn/dotnet/api/system.diagnostics.process?view=net-5.0
        /// </summary>
        /// <param name="commandStr">执行命令</param>
        public void CommandManager(string commandStr)
        {
            try
            {
                using (Process process = new Process())
                {
                    process.StartInfo.FileName = FFmpegPath;//要执行的程序名称(属性，获取或设置要启动的应用程序或文档。FileName 属性不需要表示可执行文件。它可以是其扩展名已经与系统上安装的应用程序关联的任何文件类型。)
                    process.StartInfo.Arguments = " " + commandStr;//启动该进程时传递的命令行参数
                    process.StartInfo.UseShellExecute = false;
                    process.StartInfo.RedirectStandardInput = false;//可能接受来自调用程序的输入信息  
                    process.StartInfo.RedirectStandardOutput = false;//由调用程序获取输出信息   
                    process.StartInfo.RedirectStandardError = false;//重定向标准错误输出
                    process.StartInfo.CreateNoWindow = false;//不显示程序窗口
                    process.Start();//启动程序
                    process.WaitForExit();//等待程序执行完退出进程(避免进程占用文件或者是合成文件还未生成)*
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

    }
}
